package com.burukeyou.smartdi.register;

import com.burukeyou.smartdi.support.AnnotationMeta;
import com.burukeyou.smartdi.support.AutowiredInvocation;
import com.burukeyou.smartdi.utils.AnnotationPlusUtils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.InjectionMetadata;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.Ordered;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @author caizhihao
 *
 * 接口/类	主要功能	常见应用场景
 * MergedBeanDefinitionPostProcessor	修改或增强合并后的 BeanDefinition。	动态调整 Bean 的元信息或依赖。
 * InstantiationAwareBeanPostProcessor	控制 Bean 实例化的流程，增强动态代理、属性注入能力。	AOP 代理、依赖解析。
 * BeanFactoryPostProcessor	在容器启动时修改 BeanFactory 的配置，例如添加、修改 Bean 定义。	动态注册或调整 Bean，属性占位符处理。
 * DisposableBean	提供销毁回调，用于释放资源或执行清理任务。	关闭线程池、释放文件句柄、数据库连接等。
 * AutowiredBeanProcessor	自动处理 @Autowired 注解，完成依赖的自动注入。	注入依赖、支持注解式的 DI
 *
 */
@Getter
@Slf4j
public abstract class BaseAutowiredBeanPostProcessor extends BaseSpringAware implements  Ordered,
        BeanFactoryPostProcessor,
        InstantiationAwareBeanPostProcessor,
        MergedBeanDefinitionPostProcessor,
        DisposableBean,
        AutowiredBeanProcessor
{

    // 存储需要处理的注解类型
    private final List<Class<? extends Annotation>> annotationTypes = new ArrayList<>();

    // 注解元信息缓存，用于提升性能
    private final ConcurrentMap<String,AnnotatedInjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<>(32);

    // 构造函数，初始化支持的注解类型
    protected BaseAutowiredBeanPostProcessor() {
        // 子类实现interceptAnnotation实现动态扩展支持的注解类型
        this.annotationTypes.addAll(interceptAnnotation());
    }

    /**
     * 在合并 Bean 定义阶段，处理注解元信息
     * 实现MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition方法
     * 对合并后的 BeanDefinition 进行额外的修改操作
     * 方法的用途
     * 1、自定义 Bean 属性设置
     * 在 Bean 被实例化前，对其定义元数据进行微调。例如：
     *    添加或修改属性值。
     *    设置一些注解处理逻辑。
     *
     * 2、增强依赖注入能力
     * 可以解析自定义注解，动态调整依赖关系或初始化逻辑。
     *
     * 3、优化性能
     * 通过提前处理一些静态元数据（如注解扫描结果），避免在每次注入或调用时重复解析。
     *
     * 4、自定义生命周期管理
     * 对某些特定类型的 Bean 定义特殊的初始化或销毁策略。
     *
     * @param beanDefinition 表示已合并的 RootBeanDefinition，包含 Bean 的完整元数据信息（包括父 Bean 的继承信息等）
     * @param beanType 当前 Bean 的实际类型（Class），可以用来判断或调整依赖注入行为。
     * @param beanName 当前 Bean 的名称，用于标识目标 Bean
     */
    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        // 查找并创建注解元信息
        AnnotatedInjectionMetadata injectionMetadata = findInjectionMetadata(beanName, beanType, null);
        // 校验注解元信息的配置
        injectionMetadata.checkConfigMembers(beanDefinition);
        try {
            // 在注入前执行预处理操作
            beforeInjection(injectionMetadata);
        } catch (Exception e) {
            log.error("Prepare injection of @"+getAnnotationType().getSimpleName()+" failed", e);
        }
    }

    /**
     * 移除指定 Bean 的注解元信息缓存
     */
    @Override
    public void resetBeanDefinition(String beanName) {
        this.injectionMetadataCache.remove(beanName);
    }

    /**
     * 注入前的预处理，留给子类实现
     */
    protected void beforeInjection(AnnotatedInjectionMetadata metadata) {

    }

    /**
     * 在属性设置阶段完成依赖注入
     *
     * InstantiationAwareBeanPostProcessor.postProcessProperties 对象初始化前
     */
    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        // 查找注解元信息
        AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
        try {
            // 注入前预处理
            beforeInjection(metadata);
            // 执行注入操作
            metadata.inject(bean, beanName, pvs);
        }
        catch (BeanCreationException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
        }
        return pvs;
    }

    /**
     * 查找或创建指定 Bean 的注解元信息
     */
    protected AnnotatedInjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs){
        String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
        AnnotatedInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
        // 判断是否需要刷新元信息
        if (needsRefreshInjectionMetadata(metadata, clazz)) {
            synchronized (this.injectionMetadataCache) {
                metadata = this.injectionMetadataCache.get(cacheKey);
                if (needsRefreshInjectionMetadata(metadata, clazz)) {
                    if (metadata != null) {
                        metadata.clear(pvs);
                    }
                    try {
                        metadata = buildAnnotatedMetadata(clazz);
                        this.injectionMetadataCache.put(cacheKey, metadata);
                    } catch (NoClassDefFoundError err) {
                        throw new IllegalStateException("Failed to introspect object class [" + clazz.getName() +
                                "] for annotation metadata: could not find class that it depends on", err);
                    }
                }
            }
        }
        return metadata;
    }

    /**
     * 构建指定类的注解元信息
     */
    private AnnotatedInjectionMetadata buildAnnotatedMetadata(Class<?> beanClass) {
        Collection<AnnotatedFieldElement> elements = findFieldAnnotationMetadata(beanClass);
        return  new AnnotatedInjectionMetadata(beanClass,elements);
    }


    /**
     * 查找类中字段上的注解元信息，只处理指定的注解的对象
     */
    protected List<AnnotatedFieldElement> findFieldAnnotationMetadata(final Class<?> beanClass){
        List<AnnotatedFieldElement> elements = new ArrayList<>();
        ReflectionUtils.doWithFields(beanClass,(field -> {
            for (Class<? extends Annotation> annotationType : annotationTypes) {
                AnnotationMeta annotationMeta = AnnotationPlusUtils.getAnnotationMeta(field, annotationType, getEnvironment(), false, true);
                if (annotationMeta != null) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        if (log.isWarnEnabled()) {
                            log.warn("@" + annotationType.getName() + " is not supported on static fields: " + field);
                        }
                        return;
                    }
                    elements.add(new AnnotatedFieldElement(field, annotationMeta));
                }
            }
        }));
        return elements;
    }

    /**
     * 判断注解元信息是否需要刷新
     */
    private boolean needsRefreshInjectionMetadata(AnnotatedInjectionMetadata metadata, Class<?> clazz) {
        return (metadata == null || metadata.needsRefresh(clazz));
    }

    /**
     * 定义处理顺序，值越小优先级越高
     */
    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE - 2;
    }


    /**
     * 在容器刷新时处理 Bean 工厂
     * BeanFactoryPostProcessor.postProcessBeanFactory 对象实例化前
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 获取所有的需要注册的对象定义
        String[] beanNames = beanFactory.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            Class<?> beanType = beanFactory.getType(beanName);
            if (beanType == null){
                continue;
            }
            // 构建指定注入注解的对象注解元信息
            AnnotatedInjectionMetadata injectionMetadata = findInjectionMetadata(beanName, beanType, null);
            if (injectionMetadata != null){
                beforeInjection(injectionMetadata);
            }
        }
    }

    /**
     * 注解元信息类，用于封装注解字段及其相关元信息
     */
    protected static class AnnotatedInjectionMetadata extends InjectionMetadata {

        private Class<?> targetClass;

        private final Collection<AnnotatedFieldElement> fieldElements;

        public AnnotatedInjectionMetadata(Class<?> targetClass, Collection<AnnotatedFieldElement> fieldElements) {
            super(targetClass, combine(fieldElements));
            this.targetClass = targetClass;
            this.fieldElements = fieldElements;
        }

        /**
         * 判断是否需要刷新元信息
         */
        @Override
        protected boolean needsRefresh(Class<?> clazz) {
            if (this.targetClass == clazz) {
                return false;
            }
            //IGNORE Spring CGLIB enhanced class
            if (targetClass.isAssignableFrom(clazz) &&  clazz.getName().contains("$$EnhancerBySpringCGLIB$$")) {
                return false;
            }
            return true;
        }

        public Collection<AnnotatedFieldElement> getFieldElements() {
            return fieldElements;
        }
    }

    private static <T> Collection<T> combine(Collection<? extends T>... elements) {
        List<T> allElements = new ArrayList<T>();
        for (Collection<? extends T> e : elements) {
            allElements.addAll(e);
        }
        return allElements;
    }

    /**
     * 注解字段元素，封装具体的字段注解元信息及注入逻辑
     */
    protected class AnnotatedInjectElement extends InjectionMetadata.InjectedElement {

        protected final AnnotationMeta annotationMeta;

        private Class<?> injectedType;

        protected AnnotatedInjectElement(Member member, PropertyDescriptor pd,AnnotationMeta annotationMeta) {
            super(member, pd);
            this.annotationMeta = annotationMeta;
            try {
                this.injectedType = getInjectedTypeFormMember();
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }

        /**
         * 执行字段或方法的注入逻辑
         */
        @Override
        protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {

            AutowiredInvocation invocation = new AutowiredInvocation();
            invocation.setTargetBean(bean);
            invocation.setTargetBeanName(beanName);
            invocation.setAnnotationMeta(annotationMeta);
            invocation.setInjectedType(injectedType);
            invocation.setInjectedMember(member);

            Object injectedObject = getInjectedBean(invocation);
            if (member instanceof Field) {
                Field field = (Field) member;
                ReflectionUtils.makeAccessible(field);
                ReflectionUtils.setField(field,bean,injectedObject);
            } else if (member instanceof Method) {
                Method method = (Method) member;
                ReflectionUtils.makeAccessible(method);
                method.invoke(bean, injectedObject);
            }
        }

        public Class<?> getInjectedTypeFormMember() throws ClassNotFoundException {
            if (injectedType == null) {
                if (this.isField) {
                    injectedType = ((Field) this.member).getType();
                }
                else if (this.pd != null) {
                    return this.pd.getPropertyType();
                }
                else {
                    Method method = (Method) this.member;
                    if (method.getParameterTypes().length > 0) {
                        injectedType = method.getParameterTypes()[0];
                    } else {
                        throw new IllegalStateException("get injected type failed");
                    }
                }
            }
            return injectedType;
        }

        public AnnotationMeta getAnnotationMeta() {
            return annotationMeta;
        }
    }

    public class AnnotatedFieldElement extends AnnotatedInjectElement {

        protected final Field field;

        public AnnotatedFieldElement(Field field, AnnotationMeta annotationMeta) {
            super(field,null,annotationMeta);
            this.field = field;
        }

        public Field getField() {
            return field;
        }
    }

    public final Class<? extends Annotation> getAnnotationType() {
        return annotationTypes.get(0);
    }

    @Override
    public void destroy() throws Exception {
        injectionMetadataCache.clear();
    }
}
